題目除了基本 Gallery 介面樣式以外,還有點擊後的介面
上面的圖是題目,而我們要做出幾乎一樣的樣子,題目中還有附上出題官方的CodePen,也有附上給我們解題用的template,當我們真的不會的時候,還是可以參考他們的寫法,所以沒有想像中困難。
我做好的此題CSS Challeage解答
那麼我們就開始吧。
這個題目要求我們製作一個 User Gallery
我們昨天已經把最開始的四格 User Gallery 跟滑鼠指上的效果都做出來了如下圖,現在我們就來做點擊後要打開的 info
畫面。
<div class="frame">
...
<div class="info">
<div class="panel>
<div class="eachPanel" id="panel-1">
<div class="header">
<img src="..." />
</div>
<img class="profile" src="..." />
<div class="close"></div>
<div class="bottom">...</div>
</div>
</div>
...
</div>
</div>
在 gallery
下面,我們新增另一個區塊,叫做 info
。
因為我希望點擊 pic-1
的時候,能夠開啟 #panel-1
,依此類推去開啟不同的 panel(雖然題目沒有做到這個程度),那這邊我的 HTML
就以 #panel-1
來做示範,後面的其他 panel 就依此類推。
這邊我會把 #panel-1
到 #panel-4
都加上一個名為 eachPanel
的 class 並包在 .panel
內,這是為了定位的問題。這四個 div
到時候會是 panel
內的唯一物件,一次只會顯示其中一個。
讓我說明一下:info
本人會是 position: absolute
的狀態,這樣他才能蓋在 gallery
上面。panel
則會是 position: relative
的狀態,這樣他裡面的那些元素們,才能在範圍內飛來飛去。有些人會在這邊包了四個 panel
甚至是使用 display: none
來包裝他們,但使用 display
並不能製造出位移的 transition,因此必須使用 opacity: 0
來控制顯示。
當使用了 opacity
來控制顯示,就代表這四個 div
在網頁中都有佔空間,panel-4
會被擠到很下面去根本看不到,因此這邊才會需要把它們都包在同一個 panel
內,吃同一個定位。
另外,題目上可以看出來,當我點擊 Gallery 的圖片之後,info
的內容是從上下位移進來蓋住 Gallery 畫面的,因此我可以在這邊把畫面區分成上下兩塊 div
,分別取名為 header
跟 bottom
。
再觀察題目,可以發現飛進來的動畫分成四個部分:
header
從畫面上飛進來。bottom
資訊區塊,從畫面下面飛進來。而且這四個動畫都有些許的順序,也就是它們在 click 之後,各自有先延遲一段不同的長度才開始動畫。
因此我們可以知道,這幾個項目要做在同一層比較妥當(彼此都不要成為彼此的父項)
所以 header
的部分按照題目的,包了題目提供的圖片。
接著是照片跟右上角的關閉按鈕,然後是 bottom
內放上應該要有的資訊項目跟 action button。
這樣我們基礎架構就完成了。
這邊的按鈕 icon 一樣是使用 css challenge 官方的 font-awesome 來製作,當然你也可以用自己的。
這邊可以看到我的關閉按鈕並沒有使用 font-awesome,是因為他的按鈕的樣式不符合題目要的,他的樣式比較粗也比較小,我試著調整過,但沒有讓我滿意,我希望他能跟 gallery
上面的 "+" 按鈕一樣的樣子,所以我這邊決定把 close icon 用跟 gallery
一樣的方法重做,因此這邊沒有使用 font-awesome 的 icon 來製作。
.info {
z-index: 5;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
overflow: hidden;
font-size: 0;
&.active {
pointer-events: all;
}
}
那我們就先來做點擊之後顯示或隱藏的部分,我想使用 info
來控制,那麼我們就先來設計展開的 info
的樣子。
z-index: 5
:設置 .info
的堆疊順序,使其位於其他層之上。position: absolute
top: 0
left: 0
right: 0
bottom: 0
:確保 .info
元素完全覆蓋其父容器,填滿所有空間。pointer-events: none
:禁用滑鼠事件,因為這個區塊其實是永遠在最上層的,只是看的到或看不到。但也因為它在上層,我們必須隱藏他的滑鼠事件,否則下面 gallery
那一層的滑鼠事件就永遠不會被點到。因此我們先把它的滑鼠事件隱藏起來,這樣元素不會干擾點擊操作。overflow: hidden
:隱藏超出容器的內容。.active
:這是為了之後動畫作的,我預計讓 gallery
被點擊之後,在 info
的同一個 div
上加上 .active
這個 class。當 .info
被觸發時,通過 pointer-events: all
恢復滑鼠事件。.panel {
position: relative;
width: 100%;
height: 100%;
}
這邊我就先設定好 panel
的基本樣式。
那剛做完的時候長這樣:
接下來我們就一個個拆解內部的元素
$headerH: 180px;
$bottomH: 400 - $headerH;
這邊先把一些會用到的元素計算出來:
$headerH
:這是用我們第一天教的尺規小工具量出來的 header
高度。$bottomH
:這是用我們的 .frame
的高度剪掉 $headerH
就能得出下半截 bottom
的高度了。.panel {
.header {
position: absolute;
height: $headerH;
z-index: 6;
overflow: hidden;
transform: translate3d(0,0,0)
transition: all .6s ease-in .4s;
img {width: 100%; height: 100%;}
}
}
position: absolute
:將 .header
定位於父元素內部的絕對位置。height: $headerH
:設定 .header
的高度,值取自變數 $headerH
。z-index: 6
:確保 .header
具有較高的堆疊順序,我這邊是讓它放在 info
之上。overflow: hidden
:隱藏任何超出 .header
容器的內容。transform: translate3d(0,0,0)
:這邊我們看到的是最後的位置,這邊之後是要放在 .active
內的,但沒關係,我們這邊先這樣做,之後再跟 active
內的位置交換。img {width: 100%; height: 100%;}
:確保 .header
內的圖片充滿整個容器並保持比例。.panel {
...
.bottom {
@extend .header;
height: $bottomH;
background: $primary;
top: $headerH;
left: 0;
right: 0;
padding-top: 50px;
box-sizing: border-box;
transform: translate3d(0,0,0);
}
}
因為 bottom
很多屬性都跟 header
重複,所以這邊我直接 @extend
了 header
的屬性,然後我們再來做局部修改。
height: $bottomH
:設定 .bottom
的高度,使用變數 $bottomH
。background: $primary
:將 .bottom
的背景色設置為主色,取自變數 $primary
。top: $headerH
:將 .bottom
定位於 .header
的正下方,使用變數 $headerH
來確定位置。left: 0
right: 0
:確保 .bottom
在水平方向上填滿整個容器。transform: translate3d(0,0,0)
:這邊我們看到的是最後的位置,這邊之後是要放在 .active
內的,但沒關係,我們這邊先這樣做,之後再跟 active
內的位置交換。padding-top
:將區塊上方邊界留空,好讓名字的文字不會貼在 .bottom
的最上方box-sizing
:因為設定了 padding
,這邊高度會超出預期,所以使用 border-box
,讓整體仍然維持在本來期望的高度,這樣等等跑動畫的時候才不會有不預期的情形發生。<div class="bottom">
<p class="name">Julia Toth</p>
<div class="action">
<button><span class="fa fa-phone"></span></button>
<button><span class="fa fa-comment"></span></button>
<button><span class="fa fa-heart"></span></button>
</div>
</div>
這邊是 bottom
內的架構,我們就在這邊先開版。
因為這些東西會跟著 bottom
一起移動,上面三個按鈕也只有滑鼠指上效果,所以相對單純。
.name {
font-size: 16px;
font-weight: 600;
text-align: center;
}
首先是姓名,剛修改好樣式的話是長這樣:
.action {
display: flex;
margin: 0 auto;
justify-content: space-between;
width: 50%;
padding-top: 15px;
}
然後是三個按鈕外的 div
,我使用它來讓三顆按鈕水平對齊,並使用 padding
創造出與上面的名字的距離,也使用 justify-content: space-between
及 width
創造三顆按鈕之間的距離。
$actionIconWH: 45px;
button {
width: $actionIconWH;
height: $actionIconWH;
border-radius: 50%;
background-color: $primary;
border: 1px solid #fff;
color: #fff;
transition: all .2s ease-in-out;
cursor: pointer;
&:hover {
color: $primary;
background-color: #fff;
}
}
然後我們可以來做 button 的部分,我直接就寫在 button
上。
$actionIconWH
:建立一個新的變數去控管 button 的寬高。
大小與形狀:
width: $actionIconWH
height: $actionIconWH
:按鈕的寬高為 45px
(變數 $actionIconWH
的值)。border-radius: 50%
:將按鈕設置為圓形。顏色與視覺樣式:
background-color: $primary
:背景色使用 $primary
主色。border: 1px solid #fff
:邊框為 1px 的白色實線。color: #fff
:文字顏色為白色。滑鼠指上效果:
transition: all .2s ease-in-out
:所有樣式變化在 0.2 秒內平滑過渡。&:hover
:當滑鼠懸停時,按鈕背景變為白色,字體顏色變為 $primary
主色。這樣三個 button 的樣式就差不多了,那我們就來調整裡面 icon。
button {
...
.fa {
line-height: 20px;
text-align: center;
font-size: 20px;
}
}
我們先簡單粗暴的把 .fa
這個 icon 的 style 加上字體大小,設定讓它在按鈕中間置中,也設定了他的行高,但很快的就會看出不對勁。
可以看到雖然有 icon 了,但每個 icon 的大小不一樣,高低也不太相同。
這是因為舊款的 font-awesome 產生出來的 icon 並不會每個都一樣大,而且他們其實是一種字體!
你可以去網頁上按右鍵,選檢查,點到那個 icon 就可以看到他其實是一個偽類,內容是一個字,早期的 font-awesome 是這樣給他一個字體,去讓它呈現 icon 的樣子。
這時候我們能去做的,就是修改這個字:
button {
...
.fa {
line-height: 20px;
text-align: center;
&-phone {
font-size: 22px;
padding-top: 5px;
padding-left: 1px;
}
&-comment {
font-size: 20px;
padding-bottom: 2px
}
&-heart {
font-size: 19px;
padding-top: 4px;
padding-left: 1px;
}
}
}
針對每個 icon 修改它的字型大小,在需要微調的地方加上不同方向的 padding
,讓他們在視覺上儘量做到一制,在視覺上不會有大大小小或偏移的感覺就可以了。
目前剛做好的話長這樣:
由於篇幅太長了,我決定分兩篇寫,下一篇再來寫後面的照片、右上角關閉按鈕、JS開關介面及動畫的部分。
希望改變了這種按照步驟的寫法,能讓更多人看得懂,也能跟我一樣喜歡上寫CSS。
那今天就先到這裡,明天我們再繼續來玩下一集。